//
// Created by 11510 on 2025/10/2.
//
#pragma once
#include "CommandFactory.h"
#include "linenoise.h"
#include <vector>
#include <optional>
#include <string>
#include <iostream>
#include <future>
namespace xclib_client {
    class CommandManager {
    public:
        CommandManager() {
            ctx.history = &history;
            // Initialize linenoise
            linenoiseHistoryLoad(".xclib_history");
            linenoiseSetCompletionCallback(&CommandManager::completionCallbackStatic);
            // store pointer for static callback
            instancePtr = this;
        }

        ~CommandManager() {
            linenoiseHistorySave(".xclib_history");
        }

        void repl() {
            std::string prompt = "xclib-utils> ";
            while (true) {
                char *line = linenoise(prompt.c_str());
                if (!line)
                    break; // ctrl-d

                std::string sline(line);
                free(line);
                if (sline.empty())
                    continue;

                // Save history both local and linenoise
                history.push_back(sline);
                linenoiseHistoryAdd(sline.c_str());

                // simple split
                auto args = splitArgs(sline);
                if (args.empty())
                    continue;
                // global flags: look for --wait or --async at end or anywhere
                bool hasWait = false, hasAsync = false;
                std::vector<std::string> filtered;
                for (auto &a: args) {
                    if (a == "--wait") {
                        hasWait = true;
                        continue;
                    }
                    if (a == "--async") {
                        hasAsync = true;
                        continue;
                    }
                    filtered.push_back(a);
                }
                if (hasWait && hasAsync) {
                    std::cerr << "Cannot combine --wait and --async\n";
                    continue;
                }
                bool runWait = hasWait ? true : (!hasAsync); // default is wait

                std::string cmdName = filtered[0];
                if (cmdName == "exit" || cmdName == "quit")
                    break;
                if (!CommandFactory::instance().has(cmdName)) {
                    std::cerr << "Unknown command: '" << cmdName << "'. Type 'help' to list commands.\n";
                    continue;
                }
                auto cmd = CommandFactory::instance().create(cmdName, ctx);
                // pass filtered as argv-like; include program name to satisfy cxxopts's parsing expectations
                // We will pass the vector<string> directly to command's run
                auto f = cmd->run(filtered, runWait);
                // if command returned a future (i.e., was async and runWait=false) we can detach or store
                if (f.has_value()) {
                    // detach: let it run
                    f->wait(); // we may choose to wait or not; since user requested async we should NOT wait
                    // Note: above wait() contradicts async - our implementation returns future when not waiting;
                    // To keep behavior intuitive: if returned future and runWait==false, we will not wait here. But some commands may choose to return future even for wait.
                }
            }
        }

        // completion callback (linenoise style)
        static void completionCallbackStatic(const char *buf, linenoiseCompletions *lc) {
            if (!instancePtr)
                return;
            instancePtr->completionCallback(buf, lc);
        }

        void completionCallback(const char *buf, linenoiseCompletions *lc) {
            std::string s(buf);
            // only complete first token (command names) for simplicity
            auto tokens = splitArgs(s);
            if (tokens.empty()) {
                // suggest all commands
                for (auto &c: CommandFactory::instance().listCommands())
                    linenoiseAddCompletion(lc, c.c_str());
                return;
            }
            if (s.back() == ' ') return; // user typed space -> let them type args
            if (tokens.size() == 1) {
                std::string prefix = tokens[0];
                for (auto &c: CommandFactory::instance().listCommands()) {
                    if (c.rfind(prefix, 0) == 0)
                        linenoiseAddCompletion(lc, c.c_str());
                }
            }
        }

        // ------------------------- Utility: split command line into argv-like vector
        static std::vector<std::string> splitArgs(const std::string &line) {
            std::vector<std::string> out;
            std::string cur;
            bool inQuote = false;
            char quoteChar = '\0';
            for (size_t i = 0; i < line.size(); ++i) {
                char c = line[i];
                if (!inQuote && (c == '"' || c == '\'')) {
                    inQuote = true;
                    quoteChar = c;
                    continue;
                }
                if (inQuote && c == quoteChar) {
                    inQuote = false;
                    continue;
                }
                if (!inQuote && isspace((unsigned char) c)) {
                    if (!cur.empty()) {
                        out.push_back(cur);
                        cur.clear();
                    }
                    continue;
                }
                cur.push_back(c);
            }
            if (!cur.empty())
                out.push_back(cur);
            return out;
        }

    private:
        CommandContext ctx;
        std::vector<std::string> history;
        static CommandManager *instancePtr;
    };
}